#include "Grammar.h"

#include "../Regex/MetaLexer.h"

namespace GxMetaParser
{
	GrammarParser::GrammarParser(GxMetaParser::Grammar *g)
	{
		grammar = g;
		String lexProfile = 
			L"Group = {group}\n"\
			L"Priority = {priority}\n"\
			L"Epsilon = {epsilon}\n"\
			L"Left = {#left}\n"\
			L"Right = {#right}\n"\
			L"NonAssoc = {#nonassoc}\n"\
			L"Error = {error}\n"\
			L"SymbolName = {[a-zA-Z_*]\\w*}\n"\
			L"Equals = {->}\n"\
			L"LineBreak = {\\n}\n"\
			L"LBrace = {{}\n"\
			L"RBrace = {}}\n"\
			L"Number = {\\d+}\n"\
			L"#WhiteSpace = {[ \\r\\t\\v]+}\n";
		lexer = new MetaLexer(lexProfile);
	}

	GrammarParser::SymbolState GrammarParser::GetSymbolState(const String & str)
	{
		int fid = -1;
		for (int i=0; i<grammar->Symbols.Count(); i++)
		{
			if (grammar->Symbols[i].Name == str)
			{
				if (grammar->Symbols[i].Type != GrammarSymbol::Terminal)
					return NonTerminal; 
				else
					return Terminal;
			}
		}
		return NotExist;
	}

	void GrammarParser::GenerateError(const String & text, int pos)
	{
		GrammarSyntaxError err;
		err.Text = text;
		err.Position = pos;
		Errors.Add(err);
		throw 0;
	}

	LexStream::LinkNode * GrammarParser::ReadToken(TokenType type)
	{
		if (!curNode)
		{
			GenerateError(L"Unexpected end of file.",0);
		}
		if (curNode->Data.TypeID == (int)type)
		{
			LexStream::LinkNode *node = curNode;
			curNode = curNode->GetNext();
			return node;
		}
		else
		{
			String typeName;
			switch (type)
			{
			case tkSymbolName:
				typeName = L"Symbol name";
				break;
			case tkEquals:
				typeName = L"\'->\'";
				break;
			case tkLineBreak:
				typeName = L"End of line";
				break;
			}
			GenerateError(typeName + L" expected.", curNode->Data.Position);
		}
		return 0;
	}

	bool GrammarParser::ParseRule()
	{
		try
		{
			LexStream::LinkNode * node;
			RefPtr<ProductionRule> rule = new ProductionRule();
			while (curNode && curNode->Data.TypeID == tkLineBreak)
				curNode = curNode->GetNext();
			if (!curNode)
				return false;
			if (curNode->Data.TypeID == tkLeft || curNode->Data.TypeID == tkRight
				|| curNode->Data.TypeID == tkNonAssoc)
			{
				node = curNode;
				curNode = curNode->GetNext();
				GrammarSymbol::AssociativeProperty associative = GrammarSymbol::Non;
				int priority = 0;
				switch (node->Data.TypeID)
				{
				case tkLeft:
					associative = GrammarSymbol::Left;
					break;
				case tkRight:
					associative = GrammarSymbol::Right;
					break;
				case tkNonAssoc:
					associative = GrammarSymbol::Non;
					break;
				}
				node = ReadToken(tkNumber);
				priority = node->Data.Str.ToInt();

				while (node->GetNext() && curNode->Data.TypeID == tkSymbolName)
				{
					node = ReadToken(tkSymbolName);
					int sid = grammar->GetSymbolID(node->Data.Str);
					if (sid == -1)
						return false;
					if (grammar->Symbols[sid].Type != GrammarSymbol::Terminal)
						return false;
					
					grammar->Symbols[sid].Priority = priority;
					grammar->Symbols[sid].Associative = associative;
				}
				return true;
			}
			else
			{
				node = ReadToken(tkSymbolName);
				if (GetSymbolState(node->Data.Str) != Terminal)
				{
					if (node->Data.Str.GetLength() && node->Data.Str[0] == L'*')
					{
						if (grammar->StartRule == -1)
							grammar->StartRule = grammar->Rules.Count();
						else
							GenerateError(L"Start rule redefined.", node->Data.Position);
					}
					int sid = grammar->GetSymbolID(node->Data.Str);
					if (sid == -1)
						rule->NonTerminalID = grammar->RegisterNonTerminalSymbol(node->Data.Str);
					else
						rule->NonTerminalID = sid;
				}
				else
				{
					GenerateError(String(L"Symbol \"" + node->Data.Str + L"\" already defined as terminal."),
						node->Data.Position);
				}
				rule->GroupName = node->Data.Str;
				ReadToken(tkEquals);
				while (node->GetNext() && curNode->Data.TypeID != tkLineBreak && curNode->Data.TypeID != tkLBrace)
				{
					if (curNode->Data.TypeID == tkError)
						node = ReadToken(tkError);
					else if (curNode->Data.TypeID == tkEpsilon)
						node = ReadToken(tkEpsilon);
					else
						node = ReadToken(tkSymbolName);
					SymbolState state = GetSymbolState(node->Data.Str);
					if (node->Data.TypeID != tkEpsilon)
					{
						if (state == NotExist)
						{
							int sid = grammar->RegisterNonTerminalSymbol(node->Data.Str);
							rule->Symbols.Add(sid);
						}
						else
						{
							int sid = grammar->GetSymbolID(node->Data.Str);
							rule->Symbols.Add(sid);
						}
					}
				}
				for (int i=rule->Symbols.Count()-1; i>=0; i--)
				{
					if (grammar->Symbols[rule->Symbols[i]].Type == GrammarSymbol::Terminal)
					{
						rule->Associative = grammar->Symbols[rule->Symbols[i]].Associative;
						rule->Priority = grammar->Symbols[rule->Symbols[i]].Priority;
						break;
					}
				}
				node = curNode;
				if (node && node->Data.TypeID == tkLBrace)
				{
					node = ReadToken(tkLBrace);
					if (curNode && curNode->Data.TypeID == tkGroup)
					{
						node = ReadToken(tkGroup);
						node = ReadToken(tkSymbolName);
						rule->GroupName = node->Data.Str;
					}
					if (curNode && curNode->Data.TypeID == tkPriority)
					{
						node = ReadToken(tkPriority);
						node = ReadToken(tkNumber);
						rule->Priority = node->Data.Str.ToInt();
					}
					if (curNode && curNode->Data.TypeID != tkRBrace)
					{
						node = node->GetNext();
						if (node->Data.TypeID == tkLeft)
							rule->Associative = GrammarSymbol::Left;
						else if (node->Data.TypeID == tkRight)
							rule->Associative = GrammarSymbol::Right;
						else
							rule->Associative = GrammarSymbol::Non;
						curNode = curNode->GetNext();
					}
					node = ReadToken(tkRBrace);
				}

				grammar->Rules.Add(rule);
				return true;
			}
		}
		catch(...)
		{
			return false;
		}

	}

	bool GrammarParser::Parse(const String & str)
	{
		LexStream stream;
		lexer->Errors.Clear();
		if (lexer->Parse(str, stream))
		{
			curNode = stream.Begin();
			while (curNode)
			{
				if (!ParseRule())
					break;
			}
		}
		else
		{
			for (int i=0; i<lexer->Errors.Count(); i++)
			{
				GrammarSyntaxError err;
				err.Position = lexer->Errors[i].Position;
				err.Text = lexer->Errors[i].Text;
				Errors.Add(err);
			}
			return false;
		}
		
		return true;
	}
}